#include "FilterTemplate.h"
#include <string.h>
#include <QtGlobal>
void FilterTemplate::allocMemory(int cols, int rows)
{
    //cols = qBound(1, cols, 15);
    //rows = qBound(1, rows, 15);
    if(m_data != NULL)
    {
        delete[] m_data;
        m_data = NULL;
    }
    m_cols = cols;
    m_rows = rows;
    m_data = new int[cols * rows];
    for(int i = 0; i < rows; i++)
    {
        for(int j = 0; j < cols; j ++)
        {
            m_data[i * cols + j] = 0;
        }
    }
}

FilterTemplate::FilterTemplate(int rows, int cols)
{
    m_data = NULL;
    allocMemory(cols, rows);
    m_div = 1;
    m_xCenter = cols / 2;
    m_yCenter = rows / 2;
}

void FilterTemplate::buildLaplaceFilter4()
{
    int laplace[] = {0, -1, 0, -1, 5, -1, 0, -1, 0};
    allocMemory(3, 3);
    memcpy(m_data, laplace, 9 * sizeof(int));
    m_div = 1;
    m_xCenter = 1;
    m_yCenter = 1;
}

void FilterTemplate::buildLaplaceFilter8()
{
    int laplace[] = {-1, -1, -1, -1, 12, -1, -1, -1, -1};
    allocMemory(3, 3);
    memcpy(m_data, laplace, 9 * sizeof(int));
    m_div = 4;
    m_xCenter = 1;
    m_yCenter = 1;
}

void FilterTemplate::buildSmooth(int N)
{
    allocMemory(N, N);
    for(int i = 0; i < N; i ++)
    {
        for(int j = 0; j < N; j++)
        {
            set(j, i, 1);
        }
    }
    m_div = N * N;
    m_xCenter = N / 2;
    m_yCenter = N / 2;
}

void FilterTemplate::buildGauss33()
{
    int gauss[] = {1, 2, 1, 2, 4, 2, 1, 2, 1};
    allocMemory(3, 3);
    memcpy(m_data, gauss, 9 * sizeof(int));
    m_div = 16;
    m_xCenter = 1;
    m_yCenter = 1;
}

void FilterTemplate::build33()
{
    int gauss[] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    allocMemory(3, 3);
    memcpy(m_data, gauss, 9 * sizeof(int));
    m_div = 1;
    m_xCenter = 1;
    m_yCenter = 1;
}

FilterTemplate::FilterTemplate(enum FilterType type)
{
    m_data = NULL;
    switch(type)
    {
    case LAPLACE_4:
        buildLaplaceFilter4();
        break;
    case LAPLACE_8:
        buildLaplaceFilter8();
        break;
    }
}

FilterTemplate::~FilterTemplate()
{
    if(m_data != NULL)
    {
        delete[] m_data;
        m_data = NULL;
    }
}

void FilterTemplate::apply(QImage &from, QImage &to)
{
    if(from.format() == QImage::Format_Indexed8)
    {
        apply_Indexed8(from, to);
    }
    else
    {
        apply_RGB32(from, to);
    }
}

QImage FilterTemplate::apply(QImage &image)
{
    if(image.format() == QImage::Format_Indexed8)
    {
        return apply_Indexed8(image);
    }
    else if(image.format() == QImage::Format_RGB32)
    {
        return apply_RGB32(image);
    }
    return image;
}

void FilterTemplate::apply_Indexed8(QImage &from, QImage &to)
{
    m_image_stride = from.bytesPerLine();
    m_image_data = from.constBits();
    m_image_width = from.width();
    m_image_height = from.height();
    uchar *pDest = to.bits();


    for(int x = 0 + m_xCenter; x < m_image_width - (m_cols - m_xCenter) ; x++)
    {
        for(int y = 0  + m_yCenter; y < m_image_height - (m_rows - m_yCenter); y ++)
        {
            pDest[y * m_image_stride + x] = conv_Indexed8(x, y);
        }
    }
//    for(int x = 0; x < m_image_width; x++)
//    {
//        for(int y = 0; y < m_image_height; y ++)
//        {
//            pDest[y * m_image_stride + x] = conv_Bound_Indexed8(x, y);
//        }
//    }
}

QImage FilterTemplate::apply_Indexed8(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = QImage(width, height, QImage::Format_Indexed8);
    apply_Indexed8(image, ret);
    return ret;
}

void FilterTemplate::apply_RGB32(QImage &from, QImage &to)
{
    m_image_stride = from.bytesPerLine();
    m_image_data = from.constBits();
    m_image_width = from.width();
    m_image_height = from.height();
    QRgb *pDest =(QRgb *) to.bits();


    for(int x = 0 + m_xCenter; x < m_image_width - (m_cols - m_xCenter) ; x++)
    {
        for(int y = 0  + m_yCenter; y < m_image_height - (m_rows - m_yCenter); y ++)
        {
            pDest[y * m_image_width + x] = conv_RGB32(x, y);
        }
    }
}

QImage FilterTemplate::apply_RGB32(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = QImage(width, height, QImage::Format_RGB32);
    apply_RGB32(image, ret);
    return ret;
}

void FilterTemplate::medianFilter(QImage &from, QImage &to)
{
    if(from.format() == QImage::Format_Indexed8)
    {
        medianFilter_Indexed8(from, to);
    }
    else
    {
        medianFilter_RGB32(from, to);
    }
}

QImage FilterTemplate::medianFilter(QImage &image)
{
    if(image.format() == QImage::Format_Indexed8)
    {
        return medianFilter_Indexed8(image);
    }
    else if(image.format() == QImage::Format_RGB32)
    {
        return medianFilter_RGB32(image);
    }
    return image;
}

void FilterTemplate::medianFilter_Indexed8(QImage &from, QImage &to)
{
    m_image_stride = from.bytesPerLine();
    m_image_data = from.constBits();
    m_image_width = from.width();
    m_image_height = from.height();
    uchar *pDest = to.bits();
    for(int x = 0 + m_xCenter; x < m_image_width - (m_cols - m_xCenter) ; x++)
    {
        for(int y = 0  + m_yCenter; y < m_image_height - (m_rows - m_yCenter); y ++)
        {
            pDest[y * m_image_stride + x] = medianValue_Indexed8(x, y);
        }
    }
}

QImage FilterTemplate::medianFilter_Indexed8(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = QImage(width, height, QImage::Format_Indexed8);
    medianFilter_Indexed8(image, ret);
    return ret;
}

void FilterTemplate::medianFilter_RGB32(QImage &from, QImage &to)
{
    m_image_stride = from.bytesPerLine();
    m_image_data = from.constBits();
    m_image_width = from.width();
    m_image_height = from.height();
    QRgb *pDest =(QRgb *) to.bits();


    for(int x = 0 + m_xCenter; x < m_image_width - (m_cols - m_xCenter) ; x++)
    {
        for(int y = 0  + m_yCenter; y < m_image_height - (m_rows - m_yCenter); y ++)
        {
            pDest[y * m_image_width + x] = medianValue_RGB32(x, y);
        }
    }
}
QImage FilterTemplate::medianFilter_RGB32(QImage &image)
{
    int width = image.width();
    int height = image.height();
    QImage ret = QImage(width, height, QImage::Format_RGB32);
    medianFilter_RGB32(image, ret);
    return ret;
}

inline uchar FilterTemplate::conv_Indexed8(int x, int y)
{
    int value = 0;
    int xx, yy;
    for(int col = 0; col < m_cols; col++)
    {
        xx = x + col - m_xCenter;
        for(int row = 0; row < m_rows; row ++)
        {
            yy = y + row - m_yCenter;
            value += m_image_data[yy * m_image_stride + xx] * get(row, col);
        }
    }
    value /= m_div;
    value = qBound(0, value, 255);
    return value;
}

static inline void swap(uchar array[], int i, int j)
{
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}

/*插入排序*/
static void insertSort(uchar array[], int n)
{
    for(int i=1; i<n; i++)
    {
        for(int j=i; j>0; j--)
        {
            if(array[j] > array[j-1])
            {
                swap(array, j, j-1);
            }
            else
            {
                break;
            }
        }
    }
}
uchar FilterTemplate::medianValue_Indexed8(int x, int y)
{
    int xx, yy;
    uchar array[64], n = 0;
    for(int col = 0; col < m_cols; col++)
    {
        for(int row = 0; row < m_rows; row ++)
        {
            if(get(row, col) > 0)
            {
                xx = x + col - m_xCenter;
                yy = y + row - m_yCenter;
                array[n] = m_image_data[yy * m_image_stride + xx]; // 记录该点的值
                n++;
            }
        }
    }
    insertSort(array, n);
    return array[n/2];
}

inline QRgb FilterTemplate::medianValue_RGB32(int x, int y)
{
    int r = 0, g = 0, b = 0;
    int xx, yy;
    uchar arrayRed[64], arrayGreen[64], arrayBlue[64], n = 0;
    for(int col = 0; col < m_cols; col++)
    {        for(int row = 0; row < m_rows; row ++)
        {
            if(get(row, col) > 0)
            {
                xx = x + col - m_xCenter;
                yy = y + row - m_yCenter;
                arrayBlue[n] = m_image_data[yy * m_image_stride + 4 * xx]; // 记录该点的值
                arrayGreen[n] = m_image_data[yy * m_image_stride + 4 * xx + 1]; // 记录该点的值
                arrayRed[n] = m_image_data[yy * m_image_stride + 4 * xx + 2]; // 记录该点的值
                n++;
            }
        }
    }
    insertSort(arrayRed, n);
    insertSort(arrayGreen, n);
    insertSort(arrayBlue, n);
    b = arrayBlue[n/2];
    g = arrayGreen[n/2];
    r = arrayRed[n/2];
    return qRgb(r, g, b);
}

inline QRgb FilterTemplate::conv_RGB32(int x, int y)
{
    int r = 0, g = 0, b = 0;
    int xx, yy;
    for(int col = 0; col < m_cols; col++)
    {
        xx = x + col - m_xCenter;
        for(int row = 0; row < m_rows; row ++)
        {
            yy = y + row - m_yCenter;
            b += m_image_data[yy * m_image_stride + 4 * xx] * get(row, col);
            g += m_image_data[yy * m_image_stride + 4 * xx + 1] * get(row, col);
            r += m_image_data[yy * m_image_stride + 4 * xx + 2] * get(row, col);
        }
    }
    b /= m_div;
    g /= m_div;
    r /= m_div;
    b = qBound(0, b, 255);
    g = qBound(0, g, 255);
    r = qBound(0, r, 255);
    return qRgb(r, g, b);
}

inline uchar FilterTemplate::conv_Bound_Indexed8(int x, int y)
{
    int value = 0;
    int xx, yy;
    for(int col = 0; col < m_cols; col++)
    {
        xx = qBound(0, x + col - m_xCenter, m_image_width);

        for(int row = 0; row < m_rows; row ++)
        {
            yy = qBound(0, y + row - m_yCenter, m_image_height);
            value += m_image_data[yy * m_image_stride + xx] * get(row, col);
        }
    }
    value /= m_div;
    value = qBound(0, value, 255);
    return value;
}
